/**
 * Similarity:compute direct similarity between each pair of keywords
 * 1.input file:
 *   invertIndexPath: keyword,support,[record1, record2, ...] \n
 * 2.output file:
 *   simMatrixPath: keyword,weight,[(keyword_i,sim_i)(keyword_j,sim_j)...] \n
 *       weight = sim_1 + sim_2 + ...+sim_n
 */
package preprocess.similarity;
import java.io.*;
import java.util.ArrayList;
import common.Config;
import common.Word;

class InvertRecord{
	public String keyword;
	public ArrayList<Integer> records = new ArrayList<Integer>();
	public InvertRecord( String keyword,ArrayList<Integer> li ){
		this.keyword = keyword;
		this.records = li;
	}
}

public class Similarity {
		// data format:[(recod_i,record_j,...),(record_m,record_n,...)...]
		private ArrayList< SimRecord > simMatrix = new ArrayList< SimRecord >();

	/**
	 * read inverted index file,extract index of keywords and record list into ArrayList
	 */
	private ArrayList< InvertRecord > readData() throws IOException{
		String path = Config.invertIndexPath;
		ArrayList< InvertRecord > wrlist = new ArrayList< InvertRecord >();
		
		try{
			FileReader fr = new FileReader( path );
			BufferedReader br = new BufferedReader( fr );
			String line = br.readLine();

			while( line != null ){
				String[] items = line.split(",",3);

				String str = items[ items.length -1 ].trim();
				if( str.startsWith("[") && str.endsWith("]") ){
					// remove "[" and "]"
					int len = str.length();				
					String[] records = str.substring( 1, len-2 ).split(",");
					//extract records list
					ArrayList<Integer> rlist = new ArrayList<Integer>();
					try{
						for( int i=0;i<records.length;i++ ){
							rlist.add( new Integer( records[i].trim() ) );
						}
						// add a record
						String keyword= items[0].trim();
						wrlist.add( new InvertRecord( keyword,rlist ) );
					}catch( NumberFormatException e ){
						System.err.println( "Wrong record:"+str +""+e.toString());
					}
				}
				else{
					System.err.println( "Wrong records list in:"+line );
				}
				//debug
				//System.out.println( line+" completed." );
				//end debug
				line = br.readLine();
			}//end while
			br.close();
			fr.close();
		}catch( FileNotFoundException e ){
			System.err.println( path + " not found!" );
			System.exit(1);
		}
		return wrlist;
	}
	/**
	 * compute similarity of two records list,the records list is acsending ordered
	 * the records list imply occurency of a keyword
	 * @ return similarity of two records list
	 */
	private float sim( ArrayList<Integer> li,ArrayList<Integer> lj ){
		int i = 0,j = 0,count=0;
		// compute how many common items in the two list
		while( i < li.size() && j<lj.size() ){
			while( j < lj.size() && lj.get(j) < li.get(i)  ){
				j++;
			}
			if( j < lj.size() && li.get(i).equals(lj.get(j)) ){
				count++;
				j++;
			}
			i++;
		}
		return (float)count/(li.size()+lj.size()-count);
	}

	/**
	 * expand arraylist,fill in by null
	 * @param start is start index to expand
	 * @param end is end index texpandSize
	 */
	private void expandSize( int start, int end ){
		for( int i = start;i<end;i++ ){
			simMatrix.add( i,null );
		}
	}
	/**
	 * request record in similarity matrix by index,if the record doesn't exsit,
	 * contribute it with keyword then return.
	 */
	private SimRecord requestRecord( int index,String keyword,float weight ){
		SimRecord record;
		try{
			record = simMatrix.get(  index );
			// index exsit,but the value is null
			if( record == null ){
				record = new SimRecord( new Word(keyword,weight) );
				simMatrix.set( index,record );
			}
		}catch( IndexOutOfBoundsException e ){
			// index doesn't exsits
			expandSize( simMatrix.size(), index );
			record = new SimRecord( new Word(keyword,weight) );
			simMatrix.add( index,record );
		}
		return record;
	}

	/**
	 * compute similarty matrix for all keywords
	 */
	private void simMatrix( ArrayList< InvertRecord > wrlist ) throws IOException{
		for( int i=0;i<wrlist.size();i++ ){
			SimRecord record = requestRecord( i,wrlist.get(i).keyword,wrlist.get(i).records.size() );
			//debug
			//System.out.println("compute similar words for keyword: "+i);
			//end debug
			// add correlative keywords and their silimarity 
			for( int j=i+1;j<wrlist.size();j++ ){
				float theSim = sim( wrlist.get(i).records,wrlist.get(j).records );
				
				// add a correlate keyword
				if( theSim > Config.minSim ){
					record.addSimWord( wrlist.get(j).keyword, theSim );
					// add this keyword to its correlate keyword's similarity words list
					SimRecord another = requestRecord( j,wrlist.get(j).keyword,wrlist.get( j ).records.size() );
					another.addSimWord( wrlist.get(i).keyword,theSim );
				}
			} // end for2
		} // end for1
	}
	/**
	 * write similarity matrix to file:simMatrixPath
	 */
	private void writeResult() throws IOException{
		FileWriter outfile = new FileWriter( Config.simMatrixPath );
		for( int i=0;i<simMatrix.size();i++){
			if( simMatrix.get(i).numSimWords() > 0 )
				outfile.write( simMatrix.get(i).toString()+"\n" );
		}
		outfile.close();
	}
	
	/**
	 *  run similarity matrix compution and write result
	 */
	public void run() throws IOException{
		ArrayList< InvertRecord > wrlist = readData();
		System.out.println("readData finished!");
		simMatrix( wrlist );
		System.out.println("simMatrix finished!");
		writeResult();
		System.out.println("writeResult finished:"+Config.simMatrixPath);
	}
	/**
	 * @param args
	 */
	public static void main( String[] args ) throws IOException{
		Similarity simCompution = new Similarity();
		simCompution.run();		
		System.out.println("2.Compute similarity of keywords Completed!");
	}
}
